﻿using ArtMath.Attribute;
using ArtMath.Component.Annotation;
using ArtMath.Converter;
using ArtMath.Core.Data;
using ArtMath.Core.Helper;
using ArtMath.Data;
using ArtMath.CommonType;
using ArtMath.Setting;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Reflection;
using System.Text;

namespace ArtMath.Component
{
    [Serializable, TypeConverter(typeof(PropertyDisplayerConverter))]
    public class PropertyDisplayer : IStringSettable, IFormatPainter<PropertyDisplayer>
    {
        bool visible, showPropertyName, fixloc;
        PlainText plain;
        Color textColor;
        public PointD? OffSet;
        public StyleState StyleState = StyleState.Normal;
        RPointS location;
        //int displayIndex;
        Base linkedObj;
        MemberInfo[] memInfos;
        string description, format;
        public PropertyDisplayer(Base obj, string propName) : this(obj, null, null, propName)
        {

        }
        public PropertyDisplayer(Base obj, string desc, string format, params string[] propNames)
        {
            linkedObj = obj;
            this.format = format;
            memInfos = new MemberInfo[propNames.Length];
            Type t = linkedObj.GetType();
            for (int i = 0; i < memInfos.Length; i++)
            {
                memInfos[i] = t.GetMember(propNames[i])[0];
            }
            if (desc == null)
            {
                desc = t.GetAttributes<MultilingualDisplayNameAttribute>(propNames[0])[0].DisplayName;
            }
            description = desc;
            if (Pad.CoordinateType == CoordinateSystemType.PolarCoordinate)
            {
                Location = new PPointS(Pad);
            }
            else
            {
                Location = new RPointS(Pad);
            }
            plain = new PlainText(getNewText())
            {
                Font = Settings.Instance.PropertyDisplayerFont,
            };
            textColor = plain.Color;
        }
        string getNewText()
        {
            if (!Visible) return null;
            StringBuilder sb = new StringBuilder();
            if (ShowPropertyName)
            {
                sb.Append($"{description}: ");
            }
            if (string.IsNullOrEmpty(format))
            {
                foreach (MemberInfo item in memInfos)
                {
                    sb.Append(getMemberValue(item));
                }
            }
            else
            {
                object[] objs = new object[memInfos.Length];
                for (int i = 0; i < objs.Length; i++)
                {
                    objs[i] = getMemberValue(memInfos[i]);
                }
                sb.AppendFormat(format, objs);
            }
            return sb.ToString();
        }
        object getMemberValue(MemberInfo info)
        {
            object val = null;
            switch (info.MemberType)
            {
                case MemberTypes.Field:
                    val = (info as FieldInfo).GetValue(linkedObj);
                    break;
                case MemberTypes.Property:
                    val = (info as PropertyInfo).GetValue(linkedObj, null);
                    break;
            }
            StringX sx = val as StringX;
            if (sx is object)
            {
                return sx.Value;
            }
            else
            {
                TypeConverterAttribute[] tca = linkedObj.GetType().GetAttributes<TypeConverterAttribute>(info.Name);
                if (tca != null)
                {
                    foreach (TypeConverterAttribute item in tca)
                    {
                        if (item.ConverterTypeName.Contains(nameof(IntelliPointConverter)))
                        {
                            return linkedObj.Pad.ShowPoint((PointD)val);
                        }
                        else if (item.ConverterTypeName.Contains(nameof(IntelliAngleConverter)))
                        {
                            return linkedObj.Pad.ShowRad((double)val);
                        }
                    }
                }
            }
            return val;
        }
        [Browsable(false)]
        public SketchPad Pad
        {
            get { return linkedObj.Pad; }
        }
        [MultilingualDisplayName("Visible"), TypeConverter(typeof(ArtMath.Converter.BooleanConverter)), DefaultValue(typeof(bool), "False")]
        public bool Visible
        {
            get { return visible; }
            set
            {
                visible = value;
                if (visible)
                {
                    RefreshText();
                    if (!OffSet.HasValue)
                    {
                        Location.SetFromPointD(Pad.PxyToMath(linkedObj.PropertiesLocationManager.PutLabel(SizeD.FromSize(plain.Size))));
                    }
                }
                Pad.Canvas.Invalidate();
            }
        }
        [MultilingualDisplayName(null, "Show", "PropertyName"), TypeConverter(typeof(ArtMath.Converter.BooleanConverter)), DefaultValue(typeof(bool), "True")]
        public bool ShowPropertyName
        {
            get { return showPropertyName; }
            set
            {
                showPropertyName = value;
                RefreshText();
            }
        }
        [MultilingualDisplayName("Text"), ReadOnly(true)]
        public string Text
        {
            get { return plain.Text; }
            set { plain.Text = value; }
        }
        [MultilingualDisplayName("Font")]
        public Font Font
        {
            get { return plain.Font; }
            set
            {
                plain.Font = value;
                Pad.Canvas.Invalidate();
            }
        }
        [MultilingualDisplayName("Color")]
        public Color Color
        {
            get { return textColor; }
            set
            {
                textColor = value;
                Pad.Canvas.Invalidate();
            }
        }
        [MultilingualDisplayName("Location")]
        public RPointS Location
        {
            get { return location; }
            set
            {
                if (location != null)
                {
                    location.CoordinateChanged -= Location_CoordinateChanged;
                }
                location = value;
                if (location != null)
                {
                    location.CoordinateChanged += Location_CoordinateChanged;
                }
            }
        }
        [MultilingualDisplayName(null, "Fix", "Location"), TypeConverter(typeof(ArtMath.Converter.BooleanConverter)), DefaultValue(typeof(bool), "False")]
        public bool FixLocation
        {
            get { return fixloc; }
            set
            {
                fixloc = value;
                linkedObj.RefreshPropertyDisplayersLocation();
            }
        }
        void Location_CoordinateChanged(PointD oldVal, PointD newVal)
        {
            OffSet = Pad.XYToPxy(newVal) - linkedObj.AnchorPoint(this);
        }
        public void RefreshText()
        {
            Text = getNewText();
            Pad.Canvas.Invalidate();
        }
        public void RefreshLocation()
        {
            if (!FixLocation && OffSet.HasValue)
            {
                Location.SetFromPointD(Pad.PxyToMath(linkedObj.AnchorPoint(this) + OffSet.Value));
            }
        }
        public void Paint(Graphics gra)
        {
            if (Visible)
            {
                if (StyleState == StyleState.Hover)
                {
                    Color cl = Color.GetReversedColor();
                    plain.Color = Settings.Instance.DisableReversedColorWhenEqualsPadBGColor && cl.BaseColorEquals(Pad.BackgroundColor) ? Color : cl;
                }
                else
                {
                    plain.Color = Color;
                }
                plain.Paint(gra, Pad.XYToPxy(Location.ToPointD()));
            }
        }
        public void Move(PointD offset)
        {
            Location.SetFromPointD(Pad.PxyToMath(Pad.XYToPxy(Location.ToPointD()) + offset));
        }
        public bool InRegion(PointD location)
        {
            return new RectangleD(Pad.XYToPxy(Location.ToPointD()), SizeD.FromSize(plain.Size)).Contains(location);
        }
        public void SetFromString(string exp)
        {
            Visible = !string.IsNullOrEmpty(exp);
            if (Visible)
            {
                string[] tokens = exp.Split(';', '；');
                bool locset = false, fontset = false, colorset = false;
                foreach (string item in tokens)
                {
                    if (!locset)
                    {
                        try
                        {
                            Location.SetFromString(item);
                            locset = true;
                            continue;
                        }
                        catch (Exception)
                        {
                        }
                    }
                    if (!fontset)
                    {
                        FontConverter fc = new FontConverter();
                        try
                        {
                            Font ft = fc.ConvertFromString(item) as Font;
                            if (ft != null)
                            {
                                Font = ft;
                                fontset = true;
                                continue;
                            }
                        }
                        catch (Exception)
                        {
                        }
                    }
                    if (!colorset)
                    {
                        ColorConverter cc = new ColorConverter();
                        try
                        {
                            Color = (Color)cc.ConvertFromString(item);
                        }
                        catch (Exception)
                        {
                        }
                    }
                }
                if (!locset && !fontset && !colorset)
                {
                    Visible = false;
                }
            }
        }
        
        public override string ToString()
        {
            if (Visible)
            {
                FontConverter fc = new FontConverter();
                ColorConverter cc = new ColorConverter();
                return $"{Location};{fc.ConvertToString(plain.Font)};{cc.ConvertToString(plain.Color)}";
            }
            else
            {
                return string.Empty;
            }
        }

        public void UseFormat(PropertyDisplayer obj)
        {
            Visible = obj.Visible;
            ShowPropertyName = obj.ShowPropertyName;
            //FixLocation = obj.FixLocation;//如果复制这两项，属性位置将跟随obj的位置，不合适
            Color = obj.Color;
            Font = obj.Font;
            //Location.UseFormat(obj.Location);
        }
        public void CoordinateSystemTypeChanged()
        {
            PointD tmp = Pad.ConvertRPointS(Location, Pad.OldCoordinate);
            if (Pad.CoordinateType == CoordinateSystemType.PolarCoordinate)
            {
                Location = new PPointS(Pad, tmp.X.ToString(), tmp.Y.ToString());
            }
            else
            {
                Location = new RPointS(Pad, tmp.X.ToString(), tmp.Y.ToString());
            }
            //tmp值必须在构造函数赋，不能在此处SetFromPointD，否则线段的名称将错位
        }
    }
}
